package com.lc.aop;

import com.lc.config.annos.Exclude;
import com.lc.context.BeanDefinition;
import com.lc.context.BeanFactory;
import com.lc.context.BeanPostProcessor;
import com.lc.datasource.DataSourceContext;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;

import javax.sql.DataSource;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

/**
 * @author liuc
 * @date 2021/4/18
 * @description
 **/
@Exclude
public class TransactionPostProcessor implements BeanPostProcessor {

    private Object PLACE_HOLDER = new Object();
    private Map<String, Object> cache = new HashMap<>();

    private BeanFactory beanFactory;

    public TransactionPostProcessor(BeanFactory beanFactory){
        this.beanFactory = beanFactory;
    }

    @Override
    public Object afterProcessor(Object obj, String beanId) {
        //判空
        if (obj == null) return obj;
        //已增强直接返回
        if(cache.get(beanId) != null) return obj;
        Object proxy = getProxy(obj,beanId);
        cache.put(beanId,proxy);
        return proxy;
    }

    private Object getProxy(Object o, String beanID){
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanID);
        if(!beanDefinition.isCglibProxy() && o.getClass().getInterfaces().length > 0){
            return getJdkProxy(o);
        }
        return getCglibProxy(o);
    }

    private Object getJdkProxy(Object o){
        return Proxy.newProxyInstance(o.getClass().getClassLoader(), o.getClass().getInterfaces(), (proxy, method, args) -> {
            DataSourceContext dataSourceContext = (DataSourceContext) beanFactory.getBean(DataSourceContext.class);
            TransactionMethod transactionMethod = new TransactionMethod(dataSourceContext,o);
            return transactionMethod.invoke(proxy,method,args);
        });
    }

    private Object getCglibProxy(Object o){
        return  Enhancer.create(o.getClass(), (MethodInterceptor) (proxy, method, objects, methodProxy) -> {
            DataSourceContext dataSourceContext = (DataSourceContext) beanFactory.getBean(DataSourceContext.class);
            TransactionMethod transactionMethod = new TransactionMethod(dataSourceContext,o);
            return transactionMethod.invoke(proxy,method,objects);
        });
    }
}
